﻿/*=====================================================================
 * Arquivo:	Objects.cs
 * 
 * Sumário: Implementação de métodos utilitários para Objetos em geral.
 * 
 * Autor:		Siddhartha Argollo
 * 
 * Copyright (C) TecnoTRENDS.  Todos os direitos reservados.
 =====================================================================*/
using System;
using System.Reflection;
using System.Runtime.Remoting;

namespace DaCredito
{
    /// <summary>
    /// Esta classe implementa métodos auxiliares para o tratamento de objetos
    /// em geral.
    /// </summary>
    public static class ObjectUtils
    {

        /// <summary>
        /// Cria um objeto a partir de um tipo e dos parâmetros de construção,
        /// mesmo que o construtor seja private.
        /// </summary>
        /// <param name="atType">Tipo do objeto a ser criado.</param>
        /// <param name="aoParams">Parâmetros de construção.</param>
        /// <param name="atParams">Tipos dos parâmetros de construção.</param>
        /// <returns>Instância do objeto criado.</returns>
        public static object Create(Type atType, object[] aoParams, Type[] atParams)
        {
            ConstructorInfo loInfo = atType.GetConstructor(
              BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
              null, atParams, null);
            return loInfo.Invoke(aoParams);
        }

        /// <summary>
        /// Cria um objeto a partir do seu tipo e nome do assembly.
        /// </summary>
        /// <param name="asAssembly">Nome do assembly.</param>
        /// <param name="asType">Nome do tipo a ser criado.</param>
        /// <param name="aoParams">Parâmetros de construção.</param>
        /// <returns>Instância do objeto criado.</returns>
        public static object Create(string asAssembly, string asType,
          params object[] aoParams)
        {
            ObjectHandle loHandle = Activator.CreateInstance(asAssembly, asType, true,
              BindingFlags.Default, null, aoParams, null, null, null);
            return loHandle.Unwrap();
        }

        /// <summary>
        /// Verifica se o tipo <c>atTarget</c> implementa a interface
        /// <c>atInterface</c>.
        /// </summary>
        /// <param name="atTarget">Tipo a ser usado.</param>
        /// <param name="atInterface">Interface a ser verificada.</param>
        /// <returns>True se o tipo <c>atTarget</c> implementa a interface
        /// <c>atInterface</c>, false caso contrário.</returns>
        public static bool Implements(Type atTarget, Type atInterface)
        {
            Type[] ltInterfaces = atTarget.GetInterfaces();
            if (ltInterfaces != null)
                for (int i = 0; i < ltInterfaces.Length; i++)
                    if (ltInterfaces[i].Equals(atInterface))
                        return true;
            return false;
        }

        /// <summary>
        /// Recupera o objeto MethodInfo associado ao método <c>asMethodName</c> do objeto
        /// <c>aoInstance</c>, que possui na assinatura os tipos dos objetos recebidos em
        /// <c>aoParams</c>.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence o método sendo procurado.</param>
        /// <param name="asMethodName">Nome do método desejado.</param>
        /// <param name="aoParams">Objetos cujos tipos fazem parte da assinatura do 
        /// método.</param>
        /// <returns>O objeto MethodInfo associado ao método ou null se este não for
        /// encontrado.</returns>
        public static MethodInfo GetMethod(object aoInstance, string asMethodName,
          params object[] aoParams)
        {
            return aoInstance.GetType().GetMethod(asMethodName, GetTypes(aoParams));
        }

        /// <summary>
        /// Recupera o objeto MethodInfo associado ao método <c>asMethodName</c> do objeto
        /// <c>aoInstance</c>, que não possui parâmetros na assinatura, lançando uma exceção
        /// se o método não for encontrado.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence o método sendo procurado.</param>
        /// <param name="asMethodName">Nome do método desejado.</param>
        /// <exception cref="System.InvalidOperationException">T
        /// Exceção lançada se o método <c>asMethodName</c> não for encontrado.
        /// </exception>
        /// <returns>O objeto MethodInfo associado ao método.</returns>
        public static MethodInfo ForceMethod(object aoInstance, string asMethodName)
        {
            return ForceMethod(aoInstance, asMethodName, null);
        }

        /// <summary>
        /// Recupera o objeto MethodInfo associado ao método <c>asMethodName</c> do objeto
        /// <c>aoInstance</c>, que possui na assinatura os tipos dos objetos recebidos em
        /// <c>aoParams</c>, lançando uma exceção se o método não for encontrado.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence o método sendo procurado.</param>
        /// <param name="asMethodName">Nome do método desejado.</param>
        /// <param name="aoParams">Objetos cujos tipos fazem parte da assinatura do 
        /// método; pode ser nulo para métodos sem parâmetros.</param>
        /// <exception cref="System.InvalidOperationException">T
        /// Exceção lançada se o método <c>asMethodName</c> não for encontrado.
        /// </exception>
        /// <returns>O objeto MethodInfo associado ao método.</returns>
        public static MethodInfo ForceMethod(object aoInstance, string asMethodName,
          params object[] aoParams)
        {
            MethodInfo loMethod = aoInstance.GetType().GetMethod(asMethodName, GetTypes(aoParams));
            if (loMethod == null)
                throw new InvalidOperationException("O método \"" + asMethodName +
                  "\" não foi encontrado");
            return loMethod;
        }

        /// <summary>
        /// Executa o método <c>asMethodName</c> no contexto do objeto <c>aoInstance</c>.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence o método desejado.</param>
        /// <param name="asMethodName">Nome do método a ser executado.</param>
        /// <returns>Resultado retornado pelo método executado.</returns>
        public static object Invoke(object aoInstance, string asMethodName)
        {
            return Invoke(aoInstance, asMethodName, null);
        }

        /// <summary>
        /// Executa o método <c>asMethodName</c> no contexto do objeto <c>aoInstance</c>,
        /// passando-lhe os parâmetros contidos em <c>aoParams</c>.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence o método desejado.</param>
        /// <param name="asMethodName">Nome do método a ser executado.</param>
        /// <param name="aoParams">Parâmetros a serem repassados ao método.</param>
        /// <returns>Resultado retornado pelo método executado.</returns>
        public static object Invoke(object aoInstance, string asMethodName,
          params object[] aoParams)
        {
            MethodInfo loMethod;
            if (aoParams != null)
                loMethod = aoInstance.GetType().GetMethod(asMethodName, GetTypes(aoParams));
            else
                loMethod = aoInstance.GetType().GetMethod(asMethodName);
            if (loMethod == null)
                throw new InvalidOperationException("O método \"" + asMethodName +
                  "\" não foi encontrado");
            return loMethod.Invoke(aoInstance, aoParams);
        }

        /// <summary>
        /// Retorna um array contendo os tipos dos respectivos objetos recebidos.
        /// </summary>
        /// <param name="aoParams">Array de objetos.</param>
        /// <returns>Array contendo os tipos dos objetos recebidos.</returns>
        public static Type[] GetTypes(params object[] aoParams)
        {
            Type[] ltParams;
            if (!(aoParams == null) || (aoParams.Length == 0))
            {
                ltParams = new Type[aoParams.Length];
                for (int i = 0; i < aoParams.Length; i++)
                    if (aoParams[i] == null)
                        ltParams[i] = null;
                    else
                        ltParams[i] = aoParams[i].GetType();
            }
            else
                ltParams = new Type[0];
            return ltParams;
        }

        /// <summary>
        /// Recupera o valor da propriedade <c>asPropertyName</c> no contexto do objeto
        /// <c>aoInstance</c>, lançando uma exceção se a propriedade não existir.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence a propriedade.</param>
        /// <param name="asPropertyName">Nome da propriedade recebida.</param>
        /// <exception cref="System.InvalidOperationException">T
        /// Exceção lançada se a propriedade <c>asPropertyName</c> não for encontrada.
        /// </exception>
        /// <returns>O valor da propriedade desejada.</returns>
        public static object ForceProperty(object aoInstance, string asPropertyName)
        {
            object loValue;
            if (!GetProperty(aoInstance, asPropertyName, out loValue))
                throw new InvalidOperationException("A propriedade \"" + asPropertyName +
                  "\" não foi encontrada");
            return loValue;
        }

        /// <summary>
        /// Altera o valor da propriedade <c>asPropertyName</c> no contexto do objeto
        /// <c>aoInstance</c>, lançando uma exceção se a propriedade não existir.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence a propriedade.</param>
        /// <param name="asPropertyName">Nome da propriedade recebida.</param>
        /// <param name="aoValue">Novo valor da propriedade.</param>
        /// <exception cref="System.InvalidOperationException">T
        /// Exceção lançada se a propriedade <c>asPropertyName</c> não for encontrada.
        /// </exception>
        public static void ForceProperty(object aoInstance, string asPropertyName, object aoValue)
        {
            if (!SetProperty(aoInstance, asPropertyName, aoValue))
                throw new InvalidOperationException("A propriedade \"" + asPropertyName +
                  "\" não foi encontrada");
        }

        public static Exception GetCause(Exception aoException)
        {
            Exception loCause = null;
            while (aoException != null)
            {
                loCause = aoException;
                aoException = aoException.InnerException;
            }
            return loCause;
        }

        /// <summary>
        /// Recupera o valor da propriedade <c>asPropertyName</c> no contexto do objeto
        /// <c>aoInstance</c>, se a propriedade existir.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence a propriedade.</param>
        /// <param name="asPropertyName">Nome da propriedade recebida.</param>
        /// <param name="aoValue">Valor retornado pela propriedade.</param>
        /// <returns>True se a propriedade existe, false caso contrário.</returns>
        public static bool GetProperty(object aoInstance, string asPropertyName, out object aoValue)
        {
            PropertyInfo loPropInfo = aoInstance.GetType().GetProperty(asPropertyName);
            if (loPropInfo != null)
            {
                aoValue = loPropInfo.GetValue(aoInstance, null);
                return true;
            }
            aoValue = null;
            return false;
        }

        /// <summary>
        /// Altera o valor da propriedade <c>asPropertyName</c> no contexto do objeto <c>aoInstance</c>,
        /// se a propriedade existir.
        /// </summary>
        /// <param name="aoInstance">Objeto ao qual pertence a propriedade.</param>
        /// <param name="asPropertyName">Nome da propriedade recebida.</param>
        /// <param name="aoValue">Novo valor da propriedade.</param>
        /// <returns>True se a propriedade existe e foi modificada, false caso contrário.</returns>
        public static bool SetProperty(object aoInstance, string asPropertyName, object aoValue)
        {
            PropertyInfo loPropInfo = aoInstance.GetType().GetProperty(asPropertyName);
            if (loPropInfo != null)
            {
                loPropInfo.SetValue(aoInstance, aoValue, null);
                return true;
            }
            return false;
        }

        public static bool IsNumber(Object aoValue)
        {
            return IsNumber(aoValue.GetType());
        }

        public static bool IsOrdinal(Object aoValue)
        {
            return IsOrdinal(aoValue.GetType());
        }

        public static bool IsReal(Object aoValue)
        {
            return IsReal(aoValue.GetType());
        }

        public static bool IsText(Object aoValue)
        {
            return IsText(aoValue.GetType());
        }

        public static bool IsBoolean(Object aoValue)
        {
            return IsBoolean(aoValue.GetType());
        }

        public static bool IsNumber(Type atValue)
        {
            return atValue.IsAssignableFrom(typeof(Int16)) ||
              atValue.IsAssignableFrom(typeof(Int32)) ||
              atValue.IsAssignableFrom(typeof(Int64)) ||
              atValue.IsAssignableFrom(typeof(Double)) ||
              atValue.IsAssignableFrom(typeof(Byte)) ||
              atValue.IsAssignableFrom(typeof(SByte)) ||
              atValue.IsAssignableFrom(typeof(UInt16)) ||
              atValue.IsAssignableFrom(typeof(UInt32)) ||
              atValue.IsAssignableFrom(typeof(UInt64)) ||
              atValue.IsAssignableFrom(typeof(Decimal)) ||
              atValue.IsAssignableFrom(typeof(Single));
        }

        public static bool IsOrdinal(Type atValue)
        {
            return atValue.IsAssignableFrom(typeof(Int16)) ||
              atValue.IsAssignableFrom(typeof(Int32)) ||
              atValue.IsAssignableFrom(typeof(Int64)) ||
              atValue.IsAssignableFrom(typeof(Byte)) ||
              atValue.IsAssignableFrom(typeof(SByte)) ||
              atValue.IsAssignableFrom(typeof(UInt16)) ||
              atValue.IsAssignableFrom(typeof(UInt32)) ||
              atValue.IsAssignableFrom(typeof(UInt64));
        }

        public static bool IsReal(Type atValue)
        {
            return atValue.IsAssignableFrom(typeof(Double)) ||
              atValue.IsAssignableFrom(typeof(Decimal)) ||
              atValue.IsAssignableFrom(typeof(Single));
        }

        public static bool IsText(Type atValue)
        {
            return atValue.IsAssignableFrom(typeof(String)) ||
              atValue.IsAssignableFrom(typeof(Char));
        }

        public static bool IsBoolean(Type atValue)
        {
            return atValue.IsAssignableFrom(typeof(Boolean));
        }

    } // Objects

} // Trends.TFC.Util
